﻿
using System;
using System.Collections.Generic;
using System.Linq;


namespace Boilen.Primitives.Members {

    /// <summary>
    /// Writes code for a member initialization.
    /// </summary>
    public sealed class InitializationMember : TypedMember {

        /// <summary>Scope for instance members.</summary>
        public const string InstanceMemberScope = "this";

        /// <summary>Scope for OnApplyTemplate members.</summary>
        public const string OnApplyTemplateMemberScope = "OnApplyTemplate";

        private readonly string value_;
        private readonly Action<ICodeWriter> valueWriter_;
        private readonly List<Guard> guards_ = new List<Guard>( );


        /// <inheritdoc/>
        public override bool InlineDoc { get { return false; } }

        /// <inheritdoc/>
        public override Extensions UsedExtensions {
            get {
                Extensions usedExtensions = this.ReferencedExtensions
                    | (this.DependentInitializer ?? Member.Empty).UsedExtensions;
                if( this.Guards.Any( ) )
                    usedExtensions |= Extensions.Validation;
                return usedExtensions;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to write the "variable = " assignment statement.
        /// </summary>
        public bool WriteVariableAssignment { get; set; }

        /// <summary>
        /// Gets or sets the accessor scope for the member to initialize.
        /// </summary>
        /// <remarks>The default value is the <see cref="InstanceMemberScope"/>, <c>"this"</c>.</remarks>
        public string MemberScope { get; set; }

        /// <summary>
        /// Gets or sets an initializer that depends on the initialization of the current value.
        /// </summary>
        public InitializationMember DependentInitializer { get; set; }

        /// <summary>
        /// Gets or sets additional extensions referenced by the member being initialized.
        /// </summary>
        public Extensions ReferencedExtensions { get; set; }

        /// <summary>
        /// Gets the value to initialize the member with, or <null/> if the value is written manually.
        /// </summary>
        public string Value { get { return this.value_; } }

        /// <summary>
        /// Gets a value indicating whether <see cref="Value"/> is a constructor parameter or is a default value.
        /// </summary>
        public bool IsParameterValue { get { return this.Type.Length > 0; } }

        /// <summary>
        /// Gets a value indicating whether the member initializes an instance member.
        /// </summary>
        public bool IsInstance { get { return this.MemberScope == InstanceMemberScope; } }

        /// <summary>
        /// Gets a value indicating whether the member initializes an OnApplyTemplate member.
        /// </summary>
        public bool IsOnApplyTemplate { get { return this.MemberScope == OnApplyTemplateMemberScope; } }

        /// <summary>
        /// Gets the collections of guards for the member.
        /// </summary>
        public IEnumerable<Guard> Guards { get { return this.guards_.AsEnumerable( ); } }


        /// <summary>
        /// Initializes a new <see cref="InitializationMember"/> instance.
        /// </summary>
        /// <param name="name">The name of the member to initialize.</param>
        /// <param name="value">The value to initialize the member with.</param>
        public InitializationMember( string name, string value )
            : this( name, "", false, value ) { }

        /// <summary>
        /// Initializes a new <see cref="InitializationMember"/> instance.
        /// </summary>
        /// <param name="name">The name of the member to initialize.</param>
        /// <param name="parameterType">The type of the parameter used to initialize the member.</param>
        /// <param name="parameterName">The name of the parameter used to initialize the member.</param>
        public InitializationMember( string name, string parameterType, string parameterName )
            : this( name, parameterType, true, parameterName ) { }

        private InitializationMember( string name, string type, bool isTypeRequired, string value )
            : this( name, type, isTypeRequired, value, w => w.Write( value ) ) {
            Ensure.NotNullOrEmpty( value );
        }

        /// <summary>
        /// Initializes a new <see cref="InitializationMember"/> instance.
        /// </summary>
        /// <param name="name">The name of the member to initialize.</param>
        /// <param name="valueWriter">A method to write the value to initialize the member with.</param>
        public InitializationMember( string name, Action<ICodeWriter> valueWriter )
            : this( name, "", false, null, valueWriter ) { }

        private InitializationMember( string name, string type, bool isTypeRequired, string value, Action<ICodeWriter> valueWriter )
            : base( name, type, isTypeRequired ) {
            Ensure.NotNull( valueWriter );

            this.value_ = value;
            this.valueWriter_ = valueWriter;

            this.WriteVariableAssignment = true;
            this.MemberScope = InstanceMemberScope;
        }


        /// <summary>
        /// Adds guards to the member.
        /// </summary>
        public InitializationMember AddGuards( IEnumerable<Guard> newGuards ) {
            this.guards_.AddRange( newGuards );
            return this;
        }


        /// <inheritdoc/>
        protected override void WriteCore( ICodeWriter writer ) {
            string scope = this.IsOnApplyTemplate ? InstanceMemberScope : this.MemberScope;
            if( this.WriteVariableAssignment )
                writer.Write( "{0}.{1} = ", scope, this.Name );
            this.valueWriter_( writer );
            writer.WriteLine( ";" );

            if( this.DependentInitializer != null )
                this.DependentInitializer.Write( writer );
        }

    }

}
